#include <linux/module.h>

#include "testing.h"
#include "context.h"
#include "paging_api.h"

#define info(...) printk("<1> info " __VA_ARGS__)

void run_until_result (void);
void handle_addr_cmd (struct test_command *c);
struct test_command * get_current_command (void);
void restore_data (void);
void handle (struct test_command *c);
typedef void (*callback)(struct test_command *);
void do_until_result (struct test_command *c, callback func);

struct tester_data TESTER;

void send_to_host(const void *buf, int count) {
};
void print_int_info_header(void)
{
  info("%-10s%6s%9s%11s\n",
       "test",
       "int?",
       "int num",
       "err code?");
}



void print_int_info(char *name)
{
  struct interrupt_info info;
  memcpy (&info, &TESTER.interrupt_info, sizeof(TESTER.interrupt_info));
  
  info("%-10s%6s%9hu%11s\n",
       name,
       (info.interrupt ? "yes" : "no"),
       info.int_num,
       (info.has_error_code ? "yes" : "no"));
}


void *idt_page;
u64 *pml4;
void init_contexts(void) {
  save_context (&(TESTER.linux_context));
  
  pml4 = (u64 *) get_zeroed_page (GFP_DMA);
  pml4_map_kernel(pml4);
  
  idt_page = (void *) get_zeroed_page (GFP_DMA);
  init_test_idt64(idt_page);
  
  TESTER.test_context.idtr = make_test_idtr(idt_page);
  
  TESTER.test_context.cr3 = (TESTER.linux_context.cr3 & 0xfff) | virt_to_phys(pml4);
}


char glob;

asm("hmm:\n\t"
    "int $5\n\t"
);

struct test_command comm;
struct test_page pages[10];
void *temp_page;
void init_tester(void) {
  int i;
  temp_page = (void *) get_zeroed_page (GFP_KERNEL);
  temp_init(&TESTER.tmp, temp_page);
  TESTER.pages = pages;
  
  for (i=0; i<10; ++i) {
    pages[i].info.offset_mask = OFFSET_MASK_4KB;
    pages[i].info.in_memory = 1;
    pages[i].virt = (u64 *) get_zeroed_page (GFP_KERNEL);
    pages[i].phys = virt_to_phys(pages[i].virt);
  }
    
}

void set_comm(void) {
  TESTER.cmd_ind = 0;
  TESTER.commands = &comm;
  
  comm.page = 0;
  comm.has_result = 1;
  comm.cmd = CMD_READ;
  comm.vaddr = 10000;
  //asm volatile("movq $hmm, %0" : "=r" (comm.vaddr));
}


void free_memory(void) {
  int i;
  free_page((u64)pml4);
  free_page((u64)idt_page);
  free_page((u64)temp_page);
  
  for (i=0; i<10; ++i)
    free_page((u64) pages[i].virt);
}


static int my_init(void)
{
  struct test_command * c;
  
  info("begin\n");
  init_contexts();
  init_tester();
  set_comm();
  
  info("contexts initialized\n");
  
  run_until_result();
  
  print_int_info("KA");
  
  free_memory();
  
  info("glob: %d\n", (int)glob);
  info("finish\n");
  
  return 0;
}

static void my_exit(void) {
    info("exit\n");
}

module_init(my_init);
module_exit(my_exit);
